package datasorters;

import org.apache.log4j.Logger;

public class CountingSort implements DataSorter<Integer> {
    private static final Logger l = Logger.getLogger(CountingSort.class);

    private static void reverse(Integer[] data) {
        for (int i = 0; i < data.length / 2; i++) {
            int temp = data[i];
            data[i] = data[data.length - 1 - i];
            data[data.length - 1 - i] = temp;
        }
    }

    private static void sortAsc(Integer[] data) {
        int min;
        int max;
        max = min = data[0];
        for (int i = 1; i < data.length; i++) {
            if (data[i] < min) {
                min = data[i];
            }
            if (data[i] > max) {
                max = data[i];
            }
        }
        int[] auxiliary = new int[Math.abs(max - min) + 1];
        int memoryUsed = auxiliary.length;
        int timeUsed = 0;
        for (int value : data) {
            auxiliary[value - min]++;
            timeUsed++;
        }
        int index = 0;
        for (int i = 0; i < auxiliary.length; i++) {
            boolean entered = false;
            for (int j = 0; j < auxiliary[i]; j++) {
                entered = true;
                data[index++] = i + min;
                timeUsed++;
            }
            if (!entered) {
                timeUsed++;
            }
        }
        int expectedTime = data.length + auxiliary.length;
        l.info("Time used: " + timeUsed + ". Expected time: N + K = " + expectedTime);
        l.info("Memory used: " + memoryUsed + ". Expected memory: K = " + auxiliary.length);
    }

    public Integer[] sort(Integer[] data) {
        if (data == null)
            return null;
        if (data.length == 0)
            return data;

        Integer[] sortedData = new Integer[data.length];
        System.arraycopy(data, 0, sortedData, 0, data.length);

        sortAsc(sortedData);

        return sortedData;
    }

    public Integer[] sort(Integer[] data, SortType type) {
        Integer[] sortedData = sort(data);
        if (SortType.DESC.equals(type))
            reverse(sortedData);
        return sortedData;
    }
}
